package com.clp.protocol.modbus_tcp.client.pipeline;

import com.clp.protocol.modbus_tcp.client.Master;
import com.clp.protocol.modbus_tcp.client.ModbusClient;
import com.clp.protocol.modbus_tcp.mb_frame.ReqMbFrm;
import com.clp.protocol.modbus_tcp.mb_frame.RespMbFrm;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import lombok.extern.slf4j.Slf4j;

/**
 * 与主站绑定的处理器，含有一个 master 变量，该变量属于 该handler的主站的一个 channel
 */
@Slf4j
public abstract class DuplexMasterHandler extends ChannelDuplexHandler {

    // 该处理器绑定的 主站
    private Master master;
    // 所属的 eventLoop
    protected EventLoop eventLoop;
    // 上下文
    protected ChannelHandlerContext ctx;

    /**
     * 获取master，如果获取不到，会抛出异常
     * @return
     */
    protected Master master() {
        checkMasterExistence();
        return master;
    }

    /**
     * 检查主站的存在性
     */
    private void checkMasterExistence() {
        if (master != null) return;
        this.master = ModbusClient.get().getMaster(ctx.channel());
        if (master == null) {
            log.error("找不到对应的主站！");
            throw new RuntimeException("找不到对应的主站！");
        }
    }

    /**
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        this.eventLoop = ctx.channel().eventLoop();
        this.ctx = ctx;

//        LOGGER.info("[Active] ctx: " + ctx);
//        log.info("[Active]");
        whenActiveWithoutMaster(ctx);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//        LOGGER.info("[Read] ctx: " + ctx + ", msg: " + msg);
//        log.info("[Read]");
        this.ctx = ctx;
        whenRecvingMbFrm(ctx, (RespMbFrm) msg);
    }

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
//        log.info("[Write] this.ctx: " + this.ctx);
//        LOGGER.info("[Write] ctx: " + ctx + ", msg: " + msg);
//        log.info("[Write]");
        this.ctx = ctx;
        whenSendingMbFrm(ctx, (ReqMbFrm) msg, promise);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//        log.info("[Inactive]");
        this.ctx = ctx;
        whenInActive(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
//        log.info("[Exception] " + cause);
        this.ctx = ctx;
        whenExceptionCaught(ctx, cause);
    }

    /**
     *
     * @param ctx
     * @throws Exception
     */
    public abstract void whenActiveWithoutMaster(ChannelHandlerContext ctx) throws Exception;

    protected void passActive(ChannelHandlerContext ctx) {
        ctx.fireChannelActive();
    }

    public abstract void whenRecvingMbFrm(ChannelHandlerContext ctx, RespMbFrm respMbFrm);

    protected void passRecvingMbFrm(ChannelHandlerContext ctx, RespMbFrm respMbFrm) {
        ctx.fireChannelRead(respMbFrm);
    }

    public abstract void whenSendingMbFrm(ChannelHandlerContext ctx, ReqMbFrm reqMbFrm, ChannelPromise promise);

    protected void passSendingMbFrm(ChannelHandlerContext ctx, ReqMbFrm reqMbFrm, ChannelPromise promise) {
        ctx.write(reqMbFrm, promise);
    }

    public abstract void whenInActive(ChannelHandlerContext ctx) throws Exception;

    protected void passInActive(ChannelHandlerContext ctx) {
        ctx.fireChannelInactive();
    }

    public abstract void whenExceptionCaught(ChannelHandlerContext ctx, Throwable cause);

    protected void passExceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.fireExceptionCaught(cause);
    }
}
